var ENCRYPT = 1
    var DECRYPT = 0
    var ROUND = 32
    var BLOCK = 16
    var Sbox = new Array(0xd6, 0x90, 0xe9, 0xfe, 0xcc, 0xe1, 0x3d, 0xb7, 0x16,
        0xb6, 0x14, 0xc2, 0x28, 0xfb, 0x2c, 0x05, 0x2b, 0x67, 0x9a, 0x76, 0x2a,
        0xbe, 0x04, 0xc3, 0xaa, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99, 0x9c,
        0x42, 0x50, 0xf4, 0x91, 0xef, 0x98, 0x7a, 0x33, 0x54, 0x0b, 0x43, 0xed,
        0xcf, 0xac, 0x62, 0xe4, 0xb3, 0x1c, 0xa9, 0xc9, 0x08, 0xe8, 0x95, 0x80,
        0xdf, 0x94, 0xfa, 0x75, 0x8f, 0x3f, 0xa6, 0x47, 0x07, 0xa7, 0xfc, 0xf3,
        0x73, 0x17, 0xba, 0x83, 0x59, 0x3c, 0x19, 0xe6, 0x85, 0x4f, 0xa8, 0x68,
        0x6b, 0x81, 0xb2, 0x71, 0x64, 0xda, 0x8b, 0xf8, 0xeb, 0x0f, 0x4b, 0x70,
        0x56, 0x9d, 0x35, 0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, 0xd1, 0xa2, 0x25,
        0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, 0x87, 0xd4, 0x00, 0x46, 0x57, 0x9f,
        0xd3, 0x27, 0x52, 0x4c, 0x36, 0x02, 0xe7, 0xa0, 0xc4, 0xc8, 0x9e, 0xea,
        0xbf, 0x8a, 0xd2, 0x40, 0xc7, 0x38, 0xb5, 0xa3, 0xf7, 0xf2, 0xce, 0xf9,
        0x61, 0x15, 0xa1, 0xe0, 0xae, 0x5d, 0xa4, 0x9b, 0x34, 0x1a, 0x55, 0xad,
        0x93, 0x32, 0x30, 0xf5, 0x8c, 0xb1, 0xe3, 0x1d, 0xf6, 0xe2, 0x2e, 0x82,
        0x66, 0xca, 0x60, 0xc0, 0x29, 0x23, 0xab, 0x0d, 0x53, 0x4e, 0x6f, 0xd5,
        0xdb, 0x37, 0x45, 0xde, 0xfd, 0x8e, 0x2f, 0x03, 0xff, 0x6a, 0x72, 0x6d,
        0x6c, 0x5b, 0x51, 0x8d, 0x1b, 0xaf, 0x92, 0xbb, 0xdd, 0xbc, 0x7f, 0x11,
        0xd9, 0x5c, 0x41, 0x1f, 0x10, 0x5a, 0xd8, 0x0a, 0xc1, 0x31, 0x88, 0xa5,
        0xcd, 0x7b, 0xbd, 0x2d, 0x74, 0xd0, 0x12, 0xb8, 0xe5, 0xb4, 0xb0, 0x89,
        0x69, 0x97, 0x4a, 0x0c, 0x96, 0x77, 0x7e, 0x65, 0xb9, 0xf1, 0x09, 0xc5,
        0x6e, 0xc6, 0x84, 0x18, 0xf0, 0x7d, 0xec, 0x3a, 0xdc, 0x4d, 0x20, 0x79,
        0xee, 0x5f, 0x3e, 0xd7, 0xcb, 0x39, 0x48)

    var CK = new Array(0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269, 0x70777e85,
        0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9, 0xe0e7eef5, 0xfc030a11, 0x181f262d,
        0x343b4249, 0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9, 0xc0c7ced5,
        0xdce3eaf1, 0xf8ff060d, 0x141b2229, 0x30373e45, 0x4c535a61, 0x686f767d,
        0x848b9299, 0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209, 0x10171e25,
        0x2c333a41, 0x484f565d, 0x646b7279)


    function Rotl(x, y) {
        return x << y | x >>> (32 - y)
    }

    function ByteSub(A) {
        return (Sbox[A >>> 24 & 0xFF] & 0xFF) << 24 |
            (Sbox[A >>> 16 & 0xFF] & 0xFF) << 16 |
            (Sbox[A >>> 8 & 0xFF] & 0xFF) << 8 | (Sbox[A & 0xFF] & 0xFF)
    }

    function L1(B) {
        return B ^ Rotl(B, 2) ^ Rotl(B, 10) ^ Rotl(B, 18) ^ Rotl(B, 24)
    }

    function L2(B) {
        return B ^ Rotl(B, 13) ^ Rotl(B, 23)
    }

    function SMS4Crypt(Input, Output, rk) {
        var r, mid, x0, x1, x2, x3
        var x = new Array(4)
        var tmp = new Array(4)
        for (var i = 0; i < 4; i++) {
            tmp[0] = Input[0 + 4 * i] & 0xff
            tmp[1] = Input[1 + 4 * i] & 0xff
            tmp[2] = Input[2 + 4 * i] & 0xff
            tmp[3] = Input[3 + 4 * i] & 0xff
            x[i] = tmp[0] << 24 | tmp[1] << 16 | tmp[2] << 8 | tmp[3]
        }
        for (r = 0; r < 32; r += 4) {
            mid = x[1] ^ x[2] ^ x[3] ^ rk[r + 0]
            mid = ByteSub(mid)
            x[0] = x[0] ^ L1(mid) // x4

            mid = x[2] ^ x[3] ^ x[0] ^ rk[r + 1]
            mid = ByteSub(mid)
            x[1] = x[1] ^ L1(mid) // x5

            mid = x[3] ^ x[0] ^ x[1] ^ rk[r + 2]
            mid = ByteSub(mid)
            x[2] = x[2] ^ L1(mid) // x6

            mid = x[0] ^ x[1] ^ x[2] ^ rk[r + 3]
            mid = ByteSub(mid)
            x[3] = x[3] ^ L1(mid) // x7
        }

        // Reverse
        for (var j = 0; j < 16; j += 4) {
            Output[j] = (x[3 - j / 4] >>> 24 & 0xFF)
            Output[j + 1] = (x[3 - j / 4] >>> 16 & 0xFF)
            Output[j + 2] = (x[3 - j / 4] >>> 8 & 0xFF)
            Output[j + 3] = (x[3 - j / 4] & 0xFF)
        }
    }

    function SMS4KeyExt(Key, rk, CryptFlag) {
        var r, mid
        var x = new Array(4)
        var tmp = new Array(4)
        for (var i = 0; i < 4; i++) {
            tmp[0] = Key[0 + 4 * i] & 0xFF
            tmp[1] = Key[1 + 4 * i] & 0xff
            tmp[2] = Key[2 + 4 * i] & 0xff
            tmp[3] = Key[3 + 4 * i] & 0xff
            x[i] = tmp[0] << 24 | tmp[1] << 16 | tmp[2] << 8 | tmp[3]
        }
        x[0] ^= 0xa3b1bac6
        x[1] ^= 0x56aa3350
        x[2] ^= 0x677d9197
        x[3] ^= 0xb27022dc
        for (r = 0; r < 32; r += 4) {
            mid = x[1] ^ x[2] ^ x[3] ^ CK[r + 0]
            mid = ByteSub(mid)
            rk[r + 0] = x[0] ^= L2(mid) // rk0=K4

            mid = x[2] ^ x[3] ^ x[0] ^ CK[r + 1]
            mid = ByteSub(mid)
            rk[r + 1] = x[1] ^= L2(mid) // rk1=K5

            mid = x[3] ^ x[0] ^ x[1] ^ CK[r + 2]
            mid = ByteSub(mid)
            rk[r + 2] = x[2] ^= L2(mid) // rk2=K6

            mid = x[0] ^ x[1] ^ x[2] ^ CK[r + 3]
            mid = ByteSub(mid)
            rk[r + 3] = x[3] ^= L2(mid) // rk3=K7
        }

        // 解密时轮密钥使用顺序：rk31,rk30,...,rk0
        if (CryptFlag == DECRYPT) {
            for (r = 0; r < 16; r++) {
                mid = rk[r]
                rk[r] = rk[31 - r]
                rk[31 - r] = mid
            }
        }
    }
    function sms4(inn, inLen, key, out, CryptFlag) {
        var point = 0
        var round_key = new Array(ROUND)
        SMS4KeyExt(key, round_key, CryptFlag)
        var input = new Array(16)
        var output = new Array(16)

        while (inLen >= BLOCK) {
            input = copyOfRange(inn, point, point + 16)
            SMS4Crypt(input, output, round_key)
            arraycopy(output, 0, out, point, BLOCK)
            inLen -= BLOCK
            point += BLOCK
        }

        return 0
    }

    function copyOfRange(inn, start, end) {
        var input = new Array(16)
        var j = 0
        for (var i = start; i < end; i++) {
            input[j] = inn[i]
            j++
        }
        return input
    }

    function arraycopy(output, start, out, point, BLOCK) {
        for (var i = 0; i < BLOCK; i++) {
            out[point + i] = output[i]
        }
    }

    function bytesToUnicode(bs) {
        var result = ''
        var offset = 0
        if (bs.length >= 2 && bs[0] == 254 && bs[1] == 255) { offset = 2 } // delete "FE FF"
        for (var i = offset; i < bs.length; i += 2) {
            var code = bs[i + 1] + (bs[i] << 8)
            if (isNaN(code)) {
                continue
            }
            result += String.fromCharCode(code)
        }
        return result
    }
    // 用unicode编码将str转成bytes，与java对应
    function unicodetoBytes(s) {
        var result = new Array()
        if (s == null || s == '') { return result }
        result.push(254) // add "FE FF" to head 与后台统一用unicode编码
        result.push(255)
        //   result.push(0);
        for (var i = 0; i < s.length; i++) {
            var c = s.charCodeAt(i).toString(16)
            if (c.length == 1) { c = '000' + c } else if (c.length == 2) { c = '00' + c } else if (c.length == 3) { c = '0' + c }
            var var1 = parseInt(c.substring(2), 16)
            var var2 = parseInt(c.substring(0, 2), 16)
            result.push(var2)
            result.push(var1)
        }
        return result
    }

    /**
     * 只加密16位明文
     *
     * @param plaintext
     * @param key
     * @return
     */
    var cipher = new Array(16)
    function encode16(plaintext, key) {
        cipher = new Array(16)
        sms4(plaintext, 16, key, cipher, ENCRYPT)
        return cipher
    }

    /**
     * 只解密16位密文
     *
     * @param plaintext
     * @param key
     * @return
     */
    var plain = new Array(16)
    function decode16(ciphertext, key) {
        plain = new Array(16)
        sms4(ciphertext, 16, key, plain, DECRYPT)
        return plain
    }

    function encodeSMS42(plaintext, key) {
        var ciphertext = new Array(plaintext.length)
        var k = 0
        var plainLen = plaintext.length
        while (k + 16 <= plainLen) {
            var cellPlain = new Array(16)
            for (var j = 0; j < 16; j++) {
                cellPlain[j] = plaintext[j + k]
            }
            var cellCipher = encode16(cellPlain, key)
            for (var i = 0; i < cellCipher.length; i++) {
                ciphertext[k + i] = cellCipher[i]
            }

            k += 16
        }

        return ciphertext
    }

    /**
     * 不限明文长度的SMS4解密
     *
     * @param ciphertext
     * @param key
     * @return
     */
    function decodeSMS4(ciphertext, key) {
        plaintext = new Array(ciphertext.length)

        var k = 0
        var cipherLen = ciphertext.length
        while (k + 16 <= cipherLen) {
            var cellCipher = new Array(16)
            for (var i = 0; i < 16; i++) {
                cellCipher[i] = ciphertext[k + i]
            }
            var cellPlain = decode16(cellCipher, key)
            for (var i = 0; i < cellPlain.length; i++) {
                plaintext[k + i] = cellPlain[i]
            }

            k += 16
        }

        return plaintext
    }

    /**
     * 解密，获得明文字符串
     * @param ciphertext
     * @param key
     * @return
     */
    function decodeSMS4toString(ciphertext, key) {
        var plaintext = new Array(ciphertext.length)
        plaintext = decodeSMS4(ciphertext, key)
        plaintext = bytesToUnicode(plaintext)
        return plaintext
    }
    function str2Bytes(str) {
        var bytes = new Array()
        var len, c
        len = str.length
        for (var i = 0; i < len; i++) {
            c = str.charCodeAt(i)
            if (c >= 0x010000 && c <= 0x10FFFF) {
                bytes.push(((c >> 18) & 0x07) | 0xF0)
                bytes.push(((c >> 12) & 0x3F) | 0x80)
                bytes.push(((c >> 6) & 0x3F) | 0x80)
                bytes.push((c & 0x3F) | 0x80)
            } else if (c >= 0x000800 && c <= 0x00FFFF) {
                bytes.push(((c >> 12) & 0x0F) | 0xE0)
                bytes.push(((c >> 6) & 0x3F) | 0x80)
                bytes.push((c & 0x3F) | 0x80)
            } else if (c >= 0x000080 && c <= 0x0007FF) {
                bytes.push(((c >> 6) & 0x1F) | 0xC0)
                bytes.push((c & 0x3F) | 0x80)
            } else {
                bytes.push(c & 0xFF)
            }
        }
        return bytes
    }
    function bytes2Str(arr) {
        var str = ''; var _arr = arr
        for (var i = 0; i < _arr.length; i++) {
            var one = _arr[i].toString(2); var v = one.match(/^1+?(?=0)/)
            if (v && one.length == 8) {
                var bytesLength = v[0].length
                var store = _arr[i].toString(2).slice(7 - bytesLength)
                for (var st = 1; st < bytesLength; st++) {
                    store += _arr[st + i].toString(2).slice(2)
                }
                str += String.fromCharCode(parseInt(store, 2))
                i += bytesLength - 1
            } else {
                str += String.fromCharCode(_arr[i])
            }
        }
        return str
    }
    function hexStr2Bytes(str) {
        var bytes = []
        for (var i = 0; i < str.length; i += 2) {
            var b = eval('0x' + str.charAt(i) + str.charAt(i + 1))
            bytes.push(b)
        }
        return bytes
    }
    function bytes2HexStr(bytes) {
        var hexStr = ''
        for (var i = 0; i < bytes.length; i++) {
            var bs = bytes[i].toString(16)
            if (bs.length == 1) {
                bs = '0' + bs
            }
            hexStr = hexStr + bs
        }
        return hexStr
    }
    function SMS4DKKeyExt(Key, rk, CryptFlag) {
        var r, mid
        var x = []
        var tmp = []
        for (var i = 0; i < 4; i++) {
            tmp[0] = Key[0 + 4 * i] & 0xFF
            tmp[1] = Key[1 + 4 * i] & 0xff
            tmp[2] = Key[2 + 4 * i] & 0xff
            tmp[3] = Key[3 + 4 * i] & 0xff
            x[i] = tmp[0] << 24 | tmp[1] << 16 | tmp[2] << 8 | tmp[3]
        }
        x[0] ^= 0xa3b1bac6
        x[1] ^= 0x56aa3350
        x[2] ^= 0x677d9197
        x[3] ^= 0xb27022dc
        for (r = 0; r < 32; r += 4) {
            mid = x[1] ^ x[2] ^ x[3] ^ CK[r + 0]
            mid = ByteSub(mid)
            rk[r + 0] = x[0] ^= L2(mid)

            mid = x[2] ^ x[3] ^ x[0] ^ CK[r + 1]
            mid = ByteSub(mid)
            rk[r + 1] = x[1] ^= L2(mid)

            mid = x[3] ^ x[0] ^ x[1] ^ CK[r + 2]
            mid = ByteSub(mid)
            rk[r + 2] = x[2] ^= L2(mid)

            mid = x[0] ^ x[1] ^ x[2] ^ CK[r + 3]
            mid = ByteSub(mid)
            rk[r + 3] = x[3] ^= L2(mid)
        }

        if (CryptFlag == DECRYPT) {
            for (r = 0; r < 16; r++) {
                mid = rk[r]
                rk[r] = rk[31 - r]
                rk[31 - r] = mid
            }
        }
    }
    function SMS4DKCrypt(Input, Output, rk) {
        var r, mid, x0, x1, x2, x3
        var x = []
        var tmp = []
        for (var i = 0; i < 4; i++) {
            tmp[0] = Input[0 + 4 * i] & 0xff
            tmp[1] = Input[1 + 4 * i] & 0xff
            tmp[2] = Input[2 + 4 * i] & 0xff
            tmp[3] = Input[3 + 4 * i] & 0xff
            x[i] = tmp[0] << 24 | tmp[1] << 16 | tmp[2] << 8 | tmp[3]
        }
        for (r = 0; r < 32; r += 4) {
            mid = x[1] ^ x[2] ^ x[3] ^ rk[r + 0]
            mid = ByteSub(mid)
            x[0] = x[0] ^ L1(mid) // x4

            mid = x[2] ^ x[3] ^ x[0] ^ rk[r + 1]
            mid = ByteSub(mid)
            x[1] = x[1] ^ L1(mid)

            mid = x[3] ^ x[0] ^ x[1] ^ rk[r + 2]
            mid = ByteSub(mid)
            x[2] = x[2] ^ L1(mid)

            mid = x[0] ^ x[1] ^ x[2] ^ rk[r + 3]
            mid = ByteSub(mid)
            x[3] = x[3] ^ L1(mid)
        }

        for (var j = 0; j < 16; j += 4) {
            Output[j] = (x[3 - j / 4] >>> 24 & 0xFF)
            Output[j + 1] = (x[3 - j / 4] >>> 16 & 0xFF)
            Output[j + 2] = (x[3 - j / 4] >>> 8 & 0xFF)
            Output[j + 3] = (x[3 - j / 4] & 0xFF)
        }
    }
    function SMS4DK(inBytes, inLen, key, outBytes, CryptFlag) {
        var point = 0
        var roundKey = []
        SMS4DKKeyExt(key, roundKey, CryptFlag)
        var input = []
        var output = []
        while (inLen >= BLOCK) {
            input = copyOfRange(inBytes, point, point + 16)
            SMS4DKCrypt(input, output, roundKey)
            arraycopy(output, 0, outBytes, point, BLOCK)
            inLen -= BLOCK
            point += BLOCK
        }
        return 0
    }
    /**
     * 新国密算法，hex格式
     * @param plaintext
     * @param key
     * @returns
     */
    function encodeByHexSMS4(plaintext, key) {
        if (!plaintext) {
            return ''
        }
        // plaintext = encodeURIComponent(plaintext)
        key = hexStr2Bytes(key)
        var mingwenByte = str2Bytes(plaintext)
        // 补0，长度为16的倍数
        var len = mingwenByte.length % 16
        if (len > 0) {
            for (var i = len; i < 16; i++) {
                mingwenByte.push(0)
            }
        }
        var miwen = []
        SMS4DK(mingwenByte, mingwenByte.length, key, miwen, ENCRYPT)
        return bytes2HexStr(miwen).toUpperCase()
    }
    /**
     * 新国密算法，hex格式
     * @param ciphertext
     * @param key
     * @returns
     */
    function decodeByHexSMS4(ciphertext, key) {
        if (!ciphertext) {
            return ''
        }
        key = hexStr2Bytes(key)
        var miwenByte = hexStr2Bytes(ciphertext)
        var mingwen = []
        SMS4DK(miwenByte, miwenByte.length, key, mingwen, DECRYPT)
        for (var i = 0; i < mingwen.length; i++) {
            if (mingwen[i] == 0) { // 去除末尾的0
                mingwen = mingwen.slice(0, i)
                break
            }
        }
        var result = bytes2Str(mingwen)
        return decodeURIComponent(result)
    }
    // --------------------- 以下为：对外接口 -----------------------------
    /**
     * 不限明文长度的SMS4加密
     *
     * @param plaintext 待加密明文字符串
     * @param key
     * @return
     */
    function encodeSMS4(plaintext, key) {
        key = hexStr2Bytes(key)
        if (plaintext == null || plaintext == '') {
            return ''
        }
        var plaintextByUnicode = unicodetoBytes(plaintext)
        for (var i = plaintextByUnicode.length % 16; i < 16; i++) {
            plaintext += ' '
        }
        var plaintext2 = unicodetoBytes(plaintext)
        return encodeSMS42(plaintext2, key)
    }

    /**
     * 解密，获得明文字符串
     * @param ciphertext 加密的明文字符串
     * @param key
     * @return
     */
    function decodeBySMS4(ciphertext, key) {
        key = hexStr2Bytes(key)
        var res = ''
        if (typeof ciphertext === 'string') {
            if (ciphertext == '') { return '' }
            var text = ciphertext.split(',')
            var plaintext = new Array(text.length)
            for (var i = 0; i < text.length; i++) {
                plaintext[i] = parseInt(text[i])
            }
            plaintext = decodeSMS4(plaintext, key)
            plaintext = bytesToUnicode(plaintext)
            res = plaintext
        } else {
            res = decodeSMS4toString(ciphertext, key)
        }
        res = res.replace(/(\s*$)/g, '')
        return res
    }
    /**
     * 新旧加解密方法调用
     */
    const SM4 = {
        bytes2HexStr,
        encodeBySMS4: encodeSMS4,
        decodeBySMS4: decodeBySMS4,
        encodeByHexSMS4: encodeByHexSMS4, // 新
        decodeByHexSMS4: decodeByHexSMS4 // 新
    }

    var _keyArr = new Array(0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe,
        0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10);
    const key = SM4.bytes2HexStr(_keyArr);
	

module.exports = {
    xsfasd: key,
    encodeByHexSMS4: encodeByHexSMS4,
    decodeByHexSMS4: decodeByHexSMS4
}
    